package com.hyg.ndkapplication;

import android.os.SystemClock;

import java.util.concurrent.TimeUnit;

import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers;
import io.reactivex.rxjava3.core.BackpressureStrategy;
import io.reactivex.rxjava3.core.Observable;
import io.reactivex.rxjava3.core.ObservableOnSubscribe;
import io.reactivex.rxjava3.disposables.Disposable;
import io.reactivex.rxjava3.schedulers.Schedulers;

/**
 * 光测距
 * Author: HuangYuGuang
 * Date: 2024/3/5
 */
public class LightRanging {
    private String TAG = "LightRanging";

    private static LightRanging instance;

    private Disposable getHeightDisposable;
    private boolean isRead = false;
    private MeasuredHeightCallback measuredHeightCallback;

    public static LightRanging getInstance(){
        if(instance == null){
            synchronized (LightRanging.class){
                if(instance == null){
                    instance = new LightRanging();
                }
            }
        }
        return instance;
    }

    public void startShell(MeasuredHeightCallback callback){
        if(isRead && getHeightDisposable != null) return;
        if (getHeightDisposable != null && !getHeightDisposable.isDisposed()){
            isRead = false;
            getHeightDisposable.dispose();
        }
        this.measuredHeightCallback = callback;
        getHeightDisposable = Observable.create((ObservableOnSubscribe<Integer[]>) emitter -> {
            //查询i2c-7有没有连接到设备0x70
            //     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f00:          -- -- -- -- -- -- -- -- -- -- -- -- UU 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 20: -- -- -- -- -- -- -- -- -- -- -- UU -- -- -- -- 30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 40: 40 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 70: 70 -- -- -- -- -- -- --
            ShellUtils.CommandResult commandResult = ShellUtils.execCmd("busybox i2cdetect -r -y 7",true);
            if(commandResult.result == 0){
                if(commandResult.successMsg != null && commandResult.successMsg.contains("70: 70")){
                    //查询到设备0x70
                    int[] channel = {1,2,4};
                    Integer[] heights = {640,640,640};
                    int n = 0;
                    isRead = true;
                    while (isRead){
                        heights[n] = getMeasuredHeight(channel[n]);
                        emitter.onNext(heights);
                        if(++n > 2){
                            n = 0;
                        }
                    }
                }else {
                    emitter.onError(new Exception("找不到测距模块"));
                }
            }else {
                throw new Exception(commandResult.errorMsg);
            }
            emitter.onComplete();
        }).subscribeOn(Schedulers.io())
                .delay(300, TimeUnit.MILLISECONDS, true)
                .retry(10)
                .observeOn(AndroidSchedulers.mainThread())
                .toFlowable(BackpressureStrategy.LATEST)
                .subscribe(s -> {
                    if(measuredHeightCallback != null){
                        measuredHeightCallback.onHeight(s);
                    }
                }, throwable -> {
                    throwable.printStackTrace();
                    if(measuredHeightCallback != null){
                        measuredHeightCallback.onHeightError(throwable.getMessage());
                    }
                    if(getHeightDisposable != null){
                        getHeightDisposable.dispose();
                        getHeightDisposable = null;
                    }
                });
    }

    public void startJni(MeasuredHeightCallback callback){
        if(isRead && getHeightDisposable != null) return;
        if (getHeightDisposable != null && !getHeightDisposable.isDisposed()){
            isRead = false;
            getHeightDisposable.dispose();
        }
        this.measuredHeightCallback = callback;
        getHeightDisposable = Observable.create((ObservableOnSubscribe<Integer[]>) emitter -> {
            //查询i2c-7有没有连接到设备0x70
            //     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f00:          -- -- -- 06 -- 08 -- 0a -- 0c 0d -- UU 10: -- 11 12 -- 14 -- 16 -- -- 19 -- -- 1c -- -- -- 20: 20 -- -- 23 -- -- -- 27 -- -- 2a UU -- -- -- 2f 30: -- -- 32 -- 34 35 -- 37 -- -- 3a -- 3c -- -- -- 40: 40 -- -- 43 -- 45 46 47 -- 49 4a -- 4c -- 4e -- 50: 50 -- -- 53 -- -- 56 -- -- -- -- -- 5c -- 5e -- 60: 60 -- -- 63 64 -- -- 67 -- -- 6a -- 6c 6d -- -- 70: 70 -- -- 73 74 -- -- --
            //     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f00:          -- -- -- -- -- -- -- -- -- -- -- -- UU 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 20: -- -- -- -- -- -- -- -- -- -- -- UU -- -- -- -- 30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 40: 40 -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 50: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 70: 70 -- -- -- -- -- -- --
            ShellUtils.CommandResult commandResult = ShellUtils.execCmd("busybox i2cdetect -r -y 7",true);
            if(commandResult.result == 0){
                if(commandResult.successMsg != null && commandResult.successMsg.contains("70: 70")){
                    getMeasuredHeight(1);//先用shell获取一次数值，读取失败就抛异常不往下执行，防止读取失败后连续调用JNI造成闪退
                    //查询到设备0x70
                    String fd = NativeLib.openI2CDev();
                    if("ok".equals(fd)){
                        int[] channel = {1,2,4};
                        Integer[] heights = {640,640,640};
                        int n = 0;
                        isRead = true;
                        while (isRead){
                            heights[n] = NativeLib.getHeight(channel[n]);
                            emitter.onNext(heights);
                            if(++n > 2){
                                n = 0;
                            }
                            SystemClock.sleep(100);
                        }
                    }else {
                        emitter.onError(new Exception(fd));
                    }
                }else {
                    emitter.onError(new Exception("找不到测距模块"));
                }
            }else {
                throw new Exception(commandResult.errorMsg);
            }
            emitter.onComplete();
        }).subscribeOn(Schedulers.io())
                .delay(300, TimeUnit.MILLISECONDS, true)
                .retry(5)
                .observeOn(AndroidSchedulers.mainThread())
                .toFlowable(BackpressureStrategy.LATEST)
                .subscribe(s -> {
                    if(measuredHeightCallback != null){
                        measuredHeightCallback.onHeight(s);
                    }
                }, throwable -> {
                    throwable.printStackTrace();
                    if(measuredHeightCallback != null){
                        measuredHeightCallback.onHeightError(throwable.getMessage());
                    }
                    if(getHeightDisposable != null){
                        getHeightDisposable.dispose();
                        getHeightDisposable = null;
                    }
                });
    }

    /**
     * 获取测量的高度
     * 命令参考https://zhuanlan.zhihu.com/p/526544664
     * @param channel 三个传感器，三个通道（1/2/4）
     * @return 高度mm,大于630的数据是超限的数据，不能用，小于25mm也测不了
     */
    private int getMeasuredHeight(int channel) throws Exception {
//        Log.d(TAG, "选择通道" + channel);
        ShellUtils.CommandResult commandResult = ShellUtils.execCmd(String.format("busybox i2cset -f -y 7 0x70 %s",channel),true);
        if(commandResult.result != 0) throw new  Exception("i2cset 0x70 " + channel +","+ commandResult.errorMsg);
        SystemClock.sleep(20);

//        Log.d(TAG, "获取0x5E数据");
        commandResult = ShellUtils.execCmd("busybox i2cget -f -y 7 0x40 0x5E w",true);
        if(commandResult.result != 0) throw new  Exception("i2cget 0x40 0x5E " + commandResult.errorMsg);
        String result1 = commandResult.successMsg; //0x0410

//        Log.d(TAG, "获取0x35数据");
        commandResult = ShellUtils.execCmd("busybox i2cget -f -y 7 0x40 0x35 b",true);
        if(commandResult.result != 0) throw new  Exception("i2cget 0x40 0x35 " + commandResult.errorMsg);
        String result2 = commandResult.successMsg; //0x02

//        Log.d(TAG, "开始计算");
        if(result1.length() == 6 && result2.length() == 4){
            result1 = String.format("%s%s",result1.substring(4),result1.charAt(3));
            float value = Integer.parseInt(result1,16);
            int n = Integer.parseInt(result2.substring(2),16);
            return (int) (value / 16 / (1 << n) * 10);
        }else {
            LogUtils.e(TAG,String.format("read height error, 0x5E -> %s, 0x35 -> %s",result1,result2));
            return 640;
        }
    }

    public void release(){
        measuredHeightCallback = null;
        isRead = false;
        if (getHeightDisposable != null) {
            getHeightDisposable.dispose();
            getHeightDisposable = null;
        }
    }

    public interface MeasuredHeightCallback{
        void onHeight(Integer[] heights);
        void onHeightError(String msg);
    }
}
